package com.bigshen.springcloud.utils;

import java.util.concurrent.TimeUnit;

/**
 * snowFlake : 雪花算法 64位
 * 1bit不用 + 41bit时间戳 + 10bit机器id + 12bit序列号
 */
public class SnowFlakeGenerator {

    private long roomId;   // 机房id
    private long workerId; // 机器id
    private long sequence;  // 递增开始的序列

    private long lastTimestamp = -1L; // 存储上一次生成的id的时间戳

    // 10bit机器id
    private long roomIdBit = 5l;  // 占用5个bit位
    private long workerIdBit = 5l;  // 占用5个bit位
    // 12bit序列号
    private long sequenceBits = 12l; // 12bit的递增序列

    /**
     *  http://c.biancheng.net/view/784.html
     *  https://www.sojson.com/hexconvert/2to10.html
     *
     * -1 ^ (-1 << 5) :  -1 位异或 (-1 左移 5)
     *   -1二进制：为1的二进制取反再加一
     *      int类型的1 二进制(32位)： 00000000 00000000 00000000 00000001
     *      反码     -        取反： 11111111 11111111 11111111 11111110
     *      补码     -        加一:  11111111 11111111 11111111 11111111
     *  -1<<5，-1左移5位： 低位补零
     *      11111111 11111111 11111111 11100000
     *  -1 ^ (-1<<5)， -1异或(-1左移5位):
     *        11111111 11111111 11111111 11111111
     *        11111111 11111111 11111111 11100000
     * 得出:  00000000 00000000 00000000 00011111
     * 转10进制： 2^5-1 = 32-1 = 31
     *
     * -1 ^ (-1<<12) 得出: 2^12-1 =  4096-1 = 4095
     */
    private long maxRoomId = -1l ^ (-1l << roomIdBit);    // 声明roomId最大的正整数31,  同时支持31个机房
    private long maxWorkerId = -1l ^ (-1l << workerIdBit);   // 声明workerId最大的正整数31, 同时支持每个机房32台机器
    private long maxSequenceMask = -1l ^ (-1l << sequenceBits);   // 声明sequence最大的正整数4095, 每毫秒并发量4095

    private long twepoch = 1598099282395l; // 初始时间戳

    private long workerIdShift = sequenceBits; // workerId位置：左移 12 位
    private long roomIdShift = sequenceBits + workerIdBit; // roomId位置：左移 12 + 5 位
    private long timeStampShift = sequenceBits + workerIdBit + roomIdBit; // 时间戳的位置: 左移 12 + 5 + 5 位

    public SnowFlakeGenerator(long roomId, long workerId, long sequence) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException("Worker Id不符合规范！");
        }
        if (roomId > maxRoomId ||roomId < 0) {
            throw new IllegalArgumentException("Room Id不符合规范！");
        }
        this.roomId = roomId;
        this.workerId = workerId;
        this.sequence = sequence;
    }

    public synchronized long nextVal() {
        // 毫秒级内序列号不能相同
        long timestamp = System.currentTimeMillis();

        if (timestamp < lastTimestamp) {
            throw new RuntimeException("时间戳异常，不能时钟回拨！");
        }

        if (lastTimestamp == timestamp) {
            // 同一毫秒内，递增
            sequence = (sequence+1) & maxSequenceMask;
            if(sequence == 0) {
                // 为0，同一毫秒内超过最大并发量4095，等待至下一毫秒
                timestamp = waitToNextMills(timestamp);
            }
        } else {
            // 新的毫秒，sequence重置
            // sequence = 0;  //如果直接重置为0，如果并发量没那么高，每毫秒只生产1个id，会发现生产的id都是偶数，在分库分表根据id落地时，数据会都偏向一张表
            sequence = timestamp&1;
        }
        lastTimestamp = timestamp;
//       1bit不用 +             41bit时间戳                +                  10bit机器id                      + 12bit序列号
        return ( ((timestamp - twepoch) << timeStampShift) | (roomId<<roomIdShift) | (workerId<<workerIdShift) | sequence );
    }

    // 执行到下一个时间毫秒
    private long waitToNextMills(long lastTimeStamp) {
        long timeStamp = System.currentTimeMillis();
        while (timeStamp <= lastTimeStamp) {
            timeStamp = System.currentTimeMillis();
        }
        return timeStamp;
    }

    public static void main(String[] args) {

        SnowFlakeGenerator generator = new SnowFlakeGenerator(1, 1, 1);
        for (int i = 0; i < 111; i++) {
//            try {
//                TimeUnit.MILLISECONDS.sleep(1);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
            System.out.println(generator.nextVal());
        }

    }


}
